Server 3.6.0 refuses a workflow-task completion over 16 payload limits
(32 MiB by default) with 413 payload_too_large and limit_kind
workflow_task_completion_bytes. The worker treated that as a definite
completion rejection and reported a workflow-task failure whose message
("workflow task completion failed after commands were produced") makes
the Server block replay; an ordinary task failure would instead retry the
same oversized completion.
Map the typed 413 to WorkflowTaskCompletionTooLarge (a non-retryable
ServerError with the Server's limit and the encoded request size). The
worker never resends the refused completion: it completes the same task
once with a non-retryable fail_workflow command whose message starts with
"Structural limit exceeded: workflow_task_completion_bytes:", which the
Server records as a structural_limit failure, with guidance to split the
fan-out, use child workflows or continue-as-new. If that completion also
fails, the existing completion-error handling applies to it. Other 413s
and 5xx retries are unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SDK-python slice of durable-workflow/.github#228.
Problem. Server 3.6.0 (server#1152) accepts a workflow-task completion up to 16 payload limits (32 MiB by default). A larger one gets
413 payload_too_largewithlimit_kind: workflow_task_completion_bytes. The worker treated this as a definite completion rejection and calledPOST .../failwithworkflow task completion failed after commands were produced: .... The Server'sblocks_replaymatches that text, so the run was replay-blocked. A plain task failure is no better: the Server retries the task right away, replay produces the same commands, and the worker gets the same 413 again, with no end.Change.
WorkflowTaskCompletionTooLarge, a non-retryableServerErrorexported fromdurable_workflow, is raised only for the typed 413 (status 413,reason: payload_too_large,limit_kind: workflow_task_completion_bytes). It carrieslimitfrom the Server andsize, the encoded request body length the client measured (the Server body does not include the actual size).fail_workflowcommand:non_retryable: true,exception_type: WorkflowTaskCompletionTooLarge, and a message startingStructural limit exceeded: workflow_task_completion_bytes:that gives the size and the limit and says to split the fan-out, use child workflows or continue-as-new. The Server records that prefix under thestructural_limitfailure category, the documented structural-limit outcome. It does not report a workflow-task failure. If that small completion also fails, the existing completion-error handling applies to its error, with no loop.docs/sdk-reference.md.The SDK does not use the activity-task continuation completion route, so that route has nothing to change here.
Tests.
tests/test_completion_size_limit.pyuses a realClientoverhttpx.MockTransport. It checks:fail_workflowcompletion followsA replay-regression corpus fixture,
completion-size-limit-fails-run-once, records the same request sequence. CI's corpus policy requires one because the worker changed.Local, run in Docker the same way CI does: ruff, mypy, regression-corpus validation, benchmark, full unit suite (5928 passed), package build, metadata and smoke, strict docs build.
🤖 Generated with Claude Code